<?php


namespace EchoPHP\Filesystem;

use Exception;
use FilesystemIterator;
use Symfony\Component\Finder\Finder;

/**
 * 文件、目录操作类
 * @package EchoPHP\Filesystem
 */
class Filesystem {

    /**
     * 判断文件是否存在
     * @param string $path 文件路径
     * @return bool
     */
    public function exists($path) {
        return file_exists($path);
    }

    /**
     * 获取文件大小
     * @param string $path
     * @return false|int
     */
    public function size($path) {
        return filesize($path);
    }

    /**
     * 获取文件内容，可以决定是否用共享锁的方式保证读取内容安全性
     * @param $path
     * @param bool $lock
     * @return bool|false|string
     * @throws Exception
     */
    public function get($path, $lock = false) {
        if (!$this->isFile($path)) {
            throw new Exception('the file [' . $path . '] does not exist');
        }
        return $lock ? $this->sharedGet($path) : file_get_contents($path);
    }

    /**
     * 将内容写入文件
     * @param $path
     * @param $content
     * @param bool $lock
     * @return bool|int
     */
    public function put($path, $content, $lock = false) {
        return file_put_contents($path, $content, $lock ? LOCK_EX : 0);
    }

    /**
     * 删除文件
     * @param $path
     * @return bool
     */
    public function delete($path) {
        try {
            if (@unlink($path)) {
                return true;
            }
            return false;
        } catch (Eception $e) {
            return false;
        }
    }

    /**
     * 以共享锁形式获取文件内容
     * @param $path
     * @return bool|string
     */
    public function sharedGet($path) {
        $content = '';
        $handle = fopen($path, 'rb');
        if ($handle) {
            try {
                if (flock($handle, LOCK_SH)) {
                    clearstatcache(true, $path);

                    $content = fread($handle, $this->size($path) ?: 1);

                    flock($handle, LOCK_UN);
                }
            } finally {
                fclose($handle);
            }
        }

        return $content;
    }

    /**
     * 获取目录的子目录
     * @param $directory
     * @return array
     */
    public function directories($directory) {
        $directories = [];
        foreach (Finder::create()->in($directory)->directories()->depth(0)->sortByName() as $dir) {
            $directories[] = $dir->getPathname();
        }
        return $directories;
    }

    /**
     * 判断是否是一个文件
     * @param string $file
     * @return bool
     */
    public function isFile($file) {
        return is_file($file);
    }

    /**
     * 判断是否是一个目录
     * @param $directory
     * @return bool
     */
    public function isDirectory($directory) {
        return is_dir($directory);
    }

    /**
     * 判断是否可写
     * @param $path
     * @return bool
     */
    public function isWritable($path) {
        return is_writable($path);
    }

    /**
     * 判断是否可读
     * @param $path
     * @return bool
     */
    public function isReadable($path) {
        return is_readable($path);
    }

    /**
     * 创建目录
     * @param $directory
     * @param int $mode 目录权限
     * @param bool $recursive 是否迭代创建
     * @param bool $force 是否忽略错误
     * @return bool
     */
    public function makeDirectory($directory, $mode = 0755, $recursive = false, $force = false) {
        if (!$recursive) {
            return $this->mkdir($directory, $mode, $force);
        }
        return $this->mkdirRecursive($directory, $mode, $force);
    }

    /**
     * 创建目录
     * @param $directory
     * @param int $mode
     * @param bool $force
     * @return bool
     */
    protected function mkdir($directory, $mode = 0755, $force = true) {
        if (is_dir($directory)) {
            return true;
        }
        if ($force) {
            $result = @mkdir($directory, $mode);
        } else {
            $result = mkdir($directory, $mode);
        }
        if (!$result) {
            return false;
        }
        @chown($directory, 'bgshl');
        @chgrp($directory, 'nobody');
        @chmod($directory, $mode);
        return true;
    }

    /**
     * 递归创建目录
     * @param $directory
     * @param int $mode
     * @param bool $force
     * @return bool
     */
    protected function mkdirRecursive($directory, $mode = 0755, $force = false) {
        if (is_dir($directory)) {
            return true;
        }
        if (is_dir(dirname($directory))) {
            return $this->mkdir($directory, $mode, $force);
        } else {
            if (!$this->mkdirRecursive(dirname($directory), $mode, $force)) {
                return false;
            }
            return $this->makeDirectory($directory, $mode, $force);
        }
    }

    /**
     * 删除目录
     * @param $directory
     * @param bool $preserve 是否保留目录，默认false 删除所有文件后将该目录一并删除
     * @return bool
     */
    public function deleteDirectory($directory, $preserve = false) {
        if (!$this->isDirectory($directory)) {
            return false;
        }
        $items = new FilesystemIterator($directory);
        /* @var \DirectoryIterator $item */
        foreach ($items as $item) {
            if ($item->isDir() && !$item->isLink()) {
                $this->deleteDirectory($item->getPathname());
            } else {
                $this->delete($item->getPathname());
            }
        }

        if (!$preserve) {
            @rmdir($directory);
        }

        return true;
    }

}